---
layout: docs
page_title: Service Router - Configuration Entry Reference
description: >-
  The service router configuration entry kind defines where the service mesh routes requests based on L7 network information such as header or path. Use the reference guide to learn about `""service-router""` config entry parameters and how behaviors like request timeouts, retry behavior, header modification, and path rewriting can be applied to a request based on its header or path information.
---

# Service Router Configuration Entry

The `service-router` config entry kind (`ServiceRouter` on Kubernetes) controls service mesh traffic routing and
manipulation at networking layer 7 (e.g. HTTP).

If a router is not explicitly configured or is configured with no routes then
the system behaves as if a router were configured sending all traffic to a
service of the same name.

## Requirements

- Consul [service mesh](/consul/docs/connect/configuration) enabled services
- Service to service communication over the protocol `http`

## Interaction with other Config Entries

- Service router config entries are a component of [L7 Traffic
  Management](/consul/docs/connect/l7-traffic).

- Service router config entries are restricted to only services that define
  their protocol as HTTP-based via a corresponding
  [`service-defaults`](/consul/docs/connect/config-entries/service-defaults) config
  entry or globally via
  [`proxy-defaults`](/consul/docs/connect/config-entries/proxy-defaults) .

- Any route destination that omits the `ServiceSubset` field is eligible for
  splitting via a
  [`service-splitter`](/consul/docs/connect/config-entries/service-splitter) should
  one be configured for that service, otherwise resolution proceeds according
  to any configured
  [`service-resolver`](/consul/docs/connect/config-entries/service-resolver).

## UI

Once a `service-router` is successfully entered, you can view it in the UI. Service routers, service splitters, and service resolvers can all be viewed by clicking on your service then switching to the _routing_ tab.

![screenshot of service router in the UI](/img/l7-routing/Router.png)

## Sample Config Entries

### Path prefix matching

Route HTTP requests with a path starting with `/admin` to a different service:

<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>

```hcl
Kind = "service-router"
Name = "web"
Routes = [
  {
    Match {
      HTTP {
        PathPrefix = "/admin"
      }
    }

    Destination {
      Service = "admin"
    }
  },
  # NOTE: a default catch-all will send unmatched traffic to "web"
]
```

```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceRouter
metadata:
  name: web
spec:
  routes:
    - match:
        http:
          pathPrefix: /admin
      destination:
        service: admin
  # NOTE: a default catch-all will send unmatched traffic to "web"
```

```json
{
  "Kind": "service-router",
  "Name": "web",
  "Routes": [
    {
      "Match": {
        "HTTP": {
          "PathPrefix": "/admin"
        }
      },
      "Destination": {
        "Service": "admin"
      }
    }
  ]
}
```

</CodeTabs>

### Header/query parameter matching

Route HTTP requests with a special URL parameter or header to a canary subset:

<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>

```hcl
Kind = "service-router"
Name = "web"
Routes = [
  {
    Match {
      HTTP {
        Header = [
          {
            Name  = "x-debug"
            Exact = "1"
          },
        ]
      }
    }
    Destination {
      Service       = "web"
      ServiceSubset = "canary"
    }
  },
  {
    Match {
      HTTP {
        QueryParam = [
          {
            Name  = "x-debug"
            Exact = "1"
          },
        ]
      }
    }
    Destination {
      Service       = "web"
      ServiceSubset = "canary"
    }
  },
  # NOTE: a default catch-all will send unmatched traffic to "web"
]
```

```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceRouter
metadata:
  name: web
spec:
  routes:
    - match:
        http:
          header:
            - name: x-debug
              exact: '1'
      destination:
        service: web
        serviceSubset: canary
    - match:
        http:
          queryParam:
            - name: x-debug
              exact: '1'
      destination:
        service: web
        serviceSubset: canary
  # NOTE: a default catch-all will send unmatched traffic to "web"
```

```json
{
  "Kind": "service-router",
  "Name": "web",
  "Routes": [
    {
      "Match": {
        "HTTP": {
          "Header": [
            {
              "Name": "x-debug",
              "Exact": "1"
            }
          ]
        }
      },
      "Destination": {
        "Service": "web",
        "ServiceSubset": "canary"
      }
    },
    {
      "Match": {
        "HTTP": {
          "QueryParam": [
            {
              "Name": "x-debug",
              "Exact": "1"
            }
          ]
        }
      },
      "Destination": {
        "Service": "web",
        "ServiceSubset": "canary"
      }
    }
  ]
}
```

</CodeTabs>

### gRPC routing

Re-route a gRPC method to another service. Since gRPC method calls [are
HTTP/2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md), we can use an HTTP path match rule to re-route traffic:

<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>

```hcl
Kind = "service-router"
Name = "billing"
Routes = [
  {
    Match {
      HTTP {
        PathExact = "/mycompany.BillingService/GenerateInvoice"
      }
    }

    Destination {
      Service = "invoice-generator"
    }
  },
  # NOTE: a default catch-all will send unmatched traffic to "billing"
]
```

```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceRouter
metadata:
  name: billing
spec:
  routes:
    - match:
        http:
          pathExact: /mycompany.BillingService/GenerateInvoice
      destination:
        service: invoice-generator
  # NOTE: a default catch-all will send unmatched traffic to "billing"
```

```json
{
  "Kind": "service-router",
  "Name": "billing",
  "Routes": [
    {
      "Match": {
        "HTTP": {
          "PathExact": "/mycompany.BillingService/GenerateInvoice"
        }
      },
      "Destination": {
        "Service": "invoice-generator"
      }
    }
  ]
}
```

</CodeTabs>

### Retry logic

Enable retry logic by delegating this responsibility to Consul and the proxy.  Review the [`ServiceRouteDestination`](#serviceroutedestination) block for more details.

<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>


```hcl
Kind = "service-router"
Name = "orders"
Routes = [
  {
    Match{
        HTTP {
            PathPrefix = "/coffees"
        }
    }

    Destination {
        Service = "products"
        RequestTimeout = "10s"
        NumRetries = 3
        RetryOnConnectFailure = true
        RetryOn = ["reset"]
    }
  },
  {
    Match{
        HTTP {
            PathPrefix = "/orders"
        }
    }

    Destination {
        Service = "procurement"
        RequestTimeout = "10s"
        NumRetries = 3
        RetryOnConnectFailure = true
        RetryOn = ["reset"]
    }
  }
]
```

```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceRouter
metadata:
  name: orders
spec:
  routes:
    - match:
        http:
          pathExact: /coffees
      destination:
        service: products
        requestTimeout: 10s
        numRetries: 3
        retryOnConnectFailure: true
        retryOn: ['reset']
    - match:
        http:
          pathExact: /orders
      destination:
        service: procurement
        requestTimeout: 10s
        numRetries: 3
        retryOnConnectFailure: true
        retryOn: ['reset']
```


```json
{
    "Kind": "service-router",
    "Name": "orders",
    "Routes": [
        {
            "Match": {
                "HTTP": {
                    "PathPrefix": "/coffees"
                }
            },
            "Destination": {
                "NumRetries": 3,
                "RequestTimeout": "10s",
                "RetryOnConnectFailure": true,
                "RetryOn": ["reset"],
                "Service": "procurement"
            }
        },
        {
            "Match": {
                "HTTP": {
                    "PathPrefix": "/orders"
                }
            },
            "Destination": {
                "NumRetries": 3,
                "RequestTimeout": "10s",
                "RetryOnConnectFailure": true,
                "RetryOn": ["reset"],
                "Service": "procurement"
            }
        }
    ]
}

```
</CodeTabs>

## Available Fields

<ConfigEntryReference
  keys={[
    {
      name: 'apiVersion',
      description: 'Must be set to `consul.hashicorp.com/v1alpha1`',
      hcl: false,
    },
    {
      name: 'Kind',
      description: {
        hcl: 'Must be set to `service-router`',
        yaml: 'Must be set to `ServiceRouter`',
      },
    },
    {
      name: 'Name',
      description: 'Set to the name of the service being configured.',
      type: 'string: <required>',
      yaml: false,
    },
    {
      name: 'Namespace',
      type: `string: "default"`,
      enterprise: true,
      description:
        'Specifies the namespace to which the configuration entry will apply.',
      yaml: false,
    },
    {
      name: 'Partition',
      type: `string: "default"`,
      enterprise: true,
      description:
        'Specifies the admin partition to which the configuration will apply.',
      yaml: false,
    },
    {
      name: 'Meta',
      type: 'map<string|string>: nil',
      description:
        'Specifies arbitrary KV metadata pairs. Added in Consul 1.8.4.',
      yaml: false,
    },
    {
      name: 'metadata',
      children: [
        {
          name: 'name',
          description: 'Set to the name of the service being configured.',
        },
        {
          name: 'namespace',
          description:
            'If running Consul Open Source, the namespace is ignored (see [Kubernetes Namespaces in Consul OSS](/consul/docs/k8s/crds#consul-oss)). If running Consul Enterprise see [Kubernetes Namespaces in Consul Enterprise](/consul/docs/k8s/crds#consul-enterprise) for more details.',
        },
      ],
      hcl: false,
    },
    {
      name: 'Routes',
      type: 'array<ServiceRoute>',
      description: `The list of routes to consider when
                      processing L7 requests. The first route to match in the list is terminal and
                      stops further evaluation. Traffic that fails to match any of the provided
                      routes will be routed to the default service.`,
      children: [
        {
          name: 'Match',
          type: 'ServiceRouteMatch: <optional>',
          description: `A set of criteria that can
                        match incoming L7 requests. If empty or omitted it acts as a catch-all.`,
          children: [
            {
              name: 'HTTP',
              type: 'ServiceRouteHTTPMatch: <optional>',
              description: `A set of [HTTP-specific match criteria](#serviceroutehttpmatch).`,
            },
          ],
        },
        {
          name: 'Destination',
          type: 'ServiceRouteDestination: <optional>',
          description: `Controls [how to proxy](#serviceroutedestination) the matching request(s) to a service.`,
        },
      ],
    },
  ]}
/>

### `ServiceRouteHTTPMatch`

<ConfigEntryReference
  topLevel={false}
  keys={[
    {
      name: 'PathExact',
      type: 'string: ""',
      description: {
        hcl:
          'Exact path to match on the HTTP request path.<br><br> At most only one of `PathExact`, `PathPrefix`, or `PathRegex` may be configured.',
        yaml:
          'Exact path to match on the HTTP request path.<br><br> At most only one of `pathExact`, `pathPrefix`, or `pathRegex` may be configured.',
      },
    },
    {
      name: 'PathPrefix',
      type: 'string: ""',
      description: {
        hcl:
          'Path prefix to match on the HTTP request path.<br><br> At most only one of `PathExact`, `PathPrefix`, or `PathRegex` may be configured.',
        yaml:
          'Path prefix to match on the HTTP request path.<br><br> At most only one of `pathExact`, `pathPrefix`, or `pathRegex` may be configured.',
      },
    },
    {
      name: 'PathRegex',
      type: 'string: ""',
      description: {
        hcl:
          'Regular expression to match on the HTTP request path.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `PathExact`, `PathPrefix`, or `PathRegex` may be configured.',
        yaml:
          'Regular expression to match on the HTTP request path.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `pathExact`, `pathPrefix`, or `pathRegex` may be configured.',
      },
    },
    {
      name: 'Methods',
      type: 'array<string>',
      description:
        'A list of HTTP methods for which this match  applies. If unspecified all HTTP methods are matched. If provided the names must be a valid [method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods).',
    },
    {
      name: 'Header',
      type: 'array<ServiceRouteHTTPMatchHeader>)',
      description:
        'A set of criteria that can match on HTTP request headers. If more than one is configured all must match for the overall match to apply.',
      children: [
        {
          name: 'Name',
          type: 'string: <required>',
          description: 'Name of the header to match.',
        },
        {
          name: 'Present',
          type: 'bool: false',
          description: {
            hcl:
              'Match if the header with the given name is present with any value.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the header with the given name is present with any value.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
          },
        },
        {
          name: 'Exact',
          type: 'string: ""',
          description: {
            hcl:
              'Match if the header with the given name is this value.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the header with the given name is this value.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
          },
        },
        {
          name: 'Prefix',
          type: 'string: ""',
          description: {
            hcl:
              'Match if the header with the given name has this prefix.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the header with the given name has this prefix.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
          },
        },
        {
          name: 'Suffix',
          type: 'string: ""',
          description: {
            hcl:
              'Match if the header with the given name has this suffix.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the header with the given name has this suffix.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
          },
        },
        {
          name: 'Regex',
          type: 'string: ""',
          description: {
            hcl:
              'Match if the header with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the header with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
          },
        },
        {
          name: 'Invert',
          type: 'bool: false',
          description: 'Inverts the logic of the match',
        },
      ],
    },
    {
      name: 'QueryParam',
      type: 'array<ServiceRouteHTTPMatchQueryParam>)',
      description:
        'A set of criteria that can match on HTTP query parameters. If more than one is configured all must match for the overall match to apply.',
      children: [
        {
          name: 'Name',
          type: 'string: <required>',
          description: 'The name of the query parameter to match on.',
        },
        {
          name: 'Present',
          type: 'bool: false',
          description: {
            hcl:
              'Match if the query parameter with the given name is present with any value.<br><br>At most only one of `Exact`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the query parameter with the given name is present with any value.<br><br>At most only one of `exact`, `regex`, or `present` may be configured.',
          },
        },
        {
          name: 'Exact',
          type: 'string: ""',
          description: {
            hcl:
              'Match if the query parameter with the given name is this value.<br><br>At most only one of `Exact`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the query parameter with the given name is this value.<br><br>At most only one of `exact`, `regex`, or `present` may be configured.',
          },
        },
        {
          name: 'Regex',
          type: 'string: ""',
          description: {
            hcl:
              'Match if the query parameter with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br>At most only one of `Exact`, `Regex`, or `Present` may be configured.',
            yaml:
              'Match if the query parameter with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br>At most only one of `exact`, `regex`, or `present` may be configured.',
          },
        },
      ],
    },
  ]}
/>

### `ServiceRouteDestination`

<ConfigEntryReference
  topLevel={false}
  keys={[
    {
      name: 'Service',
      type: 'string: ""',
      description:
        'The service to resolve instead of the default service. If empty then the default service name is used.',
    },
    {
      name: 'ServiceSubset',
      type: 'string: ""',
      description: {
        hcl:
          "A named subset of the given service to resolve instead of the one defined as that service's `DefaultSubset`. If empty, the default subset is used.",
        yaml:
          "A named subset of the given service to resolve instead of the one defined as that service's `defaultSubset`. If empty, the default subset is used.",
      },
    },
    {
      name: 'Namespace',
      type: 'string: ""',
      description:
        'The Consul namespace to resolve the service from instead of the current namespace. If empty, the current namespace is used.',
      enterprise: true,
    },
    {
      name: 'Partition',
      type: 'string: ""',
      description:
        'The Consul admin partition to resolve the service from instead of the current partition. If empty, the current partition is used.',
      enterprise: true,
    },
    {
      name: 'PrefixRewrite',
      type: 'string: ""',
      description: {
        hcl:
          'Defines how to rewrite the HTTP request path before proxying it to its final destination.<br><br> This requires that either `Match.HTTP.PathPrefix` or `Match.HTTP.PathExact` be configured on this route.',
        yaml:
          'Defines how to rewrite the HTTP request path before proxying it to its final destination.<br><br> This requires that either `match.http.pathPrefix` or `match.http.pathExact` be configured on this route.',
      },
    },
    {
      name: 'RequestTimeout',
      type: 'duration: 0',
      description:
        'The total amount of time permitted for the entire downstream request (and retries) to be processed.',
    },
    {
      name: 'IdleTimeout',
      type: 'duration: 0',
      description:
        'The total amount of time permitted for the request stream to be idle',
    },
    {
      name: 'NumRetries',
      type: 'int: 1',
      description: {
        hcl: 'The number of times to retry the request when a retryable result occurs. You cannot set the value to `0`. To disable retries, unset all other retry settings (`RetryOnConnectFailure`, `RetryOn`, `RetryOnStatusCodes`)',
        yaml: 'The number of times to retry the request when a retryable result occurs. You cannot set the value to `0`. To disable retries, unset all other retry settings (`retryOnConnectFailure`, `retryOn`, `retryOnStatusCodes`)',
      }
    },
    {
      name: 'RetryOnConnectFailure',
      type: 'bool: false',
      description: 'Allows for connection failure errors to trigger a retry.',
    },
    {
      name: 'RetryOn',
      type: 'array<string>',
      description: `Allows Consul to retry requests when they meet one of the following sets of conditions:
                      \`5xx\`, \`gateway-error\`, \`reset\`, \`connect-failure\`, \`envoy-ratelimited\`, \`retriable-4xx\`,
                      \`refused-stream\`, \`cancelled\`, \`deadline-exceeded\`, \`internal\`, \`resource-exhausted\`, or \`unavailable\``,
    },
    {
      name: 'RetryOnStatusCodes',
      type: 'array<int>',
      description:
        'A list of HTTP response status codes that are eligible for retry.',
    },
    {
      name: 'RequestHeaders',
      type: 'HTTPHeaderModifiers: <optional>',
      description: `A set of [HTTP-specific header modification rules](/consul/docs/connect/config-entries/service-router#httpheadermodifiers)
      that will be applied to requests routed to this service.
      This cannot be used with a \`tcp\` listener.`,
    },
    {
      name: 'ResponseHeaders',
      type: 'HTTPHeaderModifiers: <optional>',
      description: `A set of [HTTP-specific header modification rules](/consul/docs/connect/config-entries/service-router#httpheadermodifiers)
      that will be applied to responses from this service.
      This cannot be used with a \`tcp\` listener.`,
    },
  ]}
/>

### `HTTPHeaderModifiers`

<ConfigEntryReference
  topLevel={false}
  keys={[
    {
      name: 'Add',
      type: 'map<string|string>: optional',
      description: `The set of key/value pairs that specify header values to add.
      Use header names as keys. Header names are _not_ case-sensitive.
      If header values with the same name already exist, the value set here will
      be appended and both will be present.
      If Envoy is used as the proxy, the value may contain
      [variable placeholders](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#custom-request-response-headers) for example
      \`%DOWNSTREAM_REMOTE_ADDRESS%\` to interpolate dynamic request
      metadata into the value added.`,
    },
    {
      name: 'Set',
      type: 'map<string|string>: optional',
      description: `The set of key/value pairs that specify header values to add.
      Use header names as keys. Header names are _not_ case-sensitive.
      If header values with the same name already exist, the value set here will
      _replace_ them.
      If Envoy is used as the proxy, the value may contain
      [variable placeholders](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#custom-request-response-headers) for example
      \`%DOWNSTREAM_REMOTE_ADDRESS%\` to interpolate dynamic request
      metadata into the value added.`,
    },
    {
      name: 'Remove',
      type: 'array<string>: optional',
      description: `The set of header names to remove. Only headers
      whose names are a <i>case-insensitive</i> exact match will be removed`,
    },
  ]}
/>

## ACLs

Configuration entries may be protected by [ACLs](/consul/docs/security/acl).

Reading a `service-router` config entry requires `service:read` on the resource.

Creating, updating, or deleting a `service-router` config entry requires
`service:write` on the resource and `service:read` on any other service referenced by
name in these fields:

- [`Routes[].Destination.Service`](#service)

## Regular Expression Syntax

The actual syntax of the regular expression fields described here is entirely
proxy-specific.

When using [Envoy](/consul/docs/connect/proxies/envoy) as a proxy (the only supported proxy in Kubernetes),
the syntax for these fields is version specific:

| Envoy Version   | Syntax                                                              |
| --------------- | ------------------------------------------------------------------- |
| 1.11.2 or newer | [documentation](https://github.com/google/re2/wiki/Syntax)          |
| 1.11.1 or older | [documentation](https://en.cppreference.com/w/cpp/regex/ecmascript) |
